package com.xpec.c4.service.group;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jgroups.conf.ConfiguratorFactory;
import org.jgroups.conf.ProtocolConfiguration;
import org.jgroups.conf.ProtocolStackConfigurator;

import com.xpec.c4.service.module.ServerProperties;
import com.xpec.c4.service.utility.Utility;

/**
 * 
 * 
 * @author lipeilin
 * @2011-11-26 下午07:02:28
 * 
 */
public class JgroupUtitly {

	private static final Log log = LogFactory.getLog(JgroupUtitly.class);

	/**
	 * 本地的IP列表
	 */
	private static List<String> addressList = new ArrayList<String>();

	/**
	 * IPv4的IP列表
	 */
	private static List<String> ipv4AddressList = new ArrayList<String>();

	/**
	 * IPV6的IP列表
	 */
	private static List<String> ipv6AddressList = new ArrayList<String>();
	
	private static String specifiedInterfaceAddress = null; //ip of specified binding ip
	
	static {
		try {

			Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface
					.getNetworkInterfaces();
			while (allNetInterfaces.hasMoreElements()) {
				NetworkInterface netInterface = (NetworkInterface) allNetInterfaces
						.nextElement();
				log.info("NetworkInterface Name:" + netInterface.getName());
				Enumeration<InetAddress> addresses = netInterface
						.getInetAddresses();
				while (addresses.hasMoreElements()) {
					InetAddress inetAddress = addresses.nextElement();

					String ip = inetAddress.getHostAddress();
					addressList.add(ip);

					if (inetAddress instanceof Inet4Address) {
						ipv4AddressList.add(ip);
						
						if(netInterface.getDisplayName().equals(ServerProperties.BIND_TO_NAME))
						{
							specifiedInterfaceAddress = ip;
						}						
					}
					if (inetAddress instanceof Inet6Address) {
						ipv6AddressList.add(ip);
					}
					
					log.info("IP:" + ip);

				}
			}

			log.info("ipv4AddressList:" + ipv4AddressList);
			log.info("ipv6AddressList:" + ipv6AddressList);
		} catch (SocketException e) {
			log.error("Unable to find IP ", e);
		}
	}

	/**
	 * 获得JGROUP的UDP配置
	 * 
	 * @param ip
	 *            自定义的IP
	 * @param port
	 *            自定义的PORT
	 * @return
	 * 
	 * @2011-11-26 下午06:59:17
	 */
	public static ProtocolStackConfigurator getJgroupsUDPConfigurator(
			String ip, int port) {
		// 加载配置文件
		// File file = new File(ServiceConfig.rootPath+"/bin/"
		// + "jgroups-udp.xml");
		URL url = Utility.getUrl("jgroups-udp.xml");
		ProtocolStackConfigurator configurator = null;

		try {
			configurator = ConfiguratorFactory.getStackConfigurator(url);
		} catch (Exception e) {
			log.error("Get Jroup UDP Protocol String error", e);
			throw new RuntimeException(e);
		}

		// 修改端口
		ProtocolConfiguration config = configurator.getProtocolStack().get(0);
		String mcastPort = config.getProperties().get("mcast_port");
		String begin = mcastPort.substring(0, mcastPort.indexOf(":") + 1);
		String end = "}";
		mcastPort = begin + port + end;
		config.getProperties().put("mcast_port", mcastPort);

		// 修改IP
		String mcastIP = config.getProperties().get("mcast_addr");
		if (mcastIP != null) {
			begin = mcastIP.substring(0, mcastIP.indexOf(":") + 1);
			end = "}";
			mcastIP = begin + ip + end;
			config.getProperties().put("mcast_addr", mcastIP);
		}

		changeBindAddress(config);

		return configurator;
	}

	/**
	 * 获得JGROUP的UDP配置
	 * 
	 * @param ip
	 *            自定义的IP
	 * @param port
	 *            自定义的PORT
	 * @return
	 * 
	 * @2011-11-26 下午06:59:17
	 */
	public static ProtocolStackConfigurator getJgroupsUDP2Configurator(
			String ip, int port) {
		// 加载配置文件
		// File file = new File(ServiceConfig.rootPath+"/bin/"
		// + "jgroups-udp.xml");
		URL url = Utility.getUrl("udp.xml");
		ProtocolStackConfigurator configurator = null;

		try {
			configurator = ConfiguratorFactory.getStackConfigurator(url);
		} catch (Exception e) {
			log.error("Get Jroup UDP Protocol String error", e);
			throw new RuntimeException(e);
		}

		// 修改端口
		ProtocolConfiguration config = configurator.getProtocolStack().get(0);
		String mcastPort = config.getProperties().get("mcast_port");
		String begin = mcastPort.substring(0, mcastPort.indexOf(":") + 1);
		String end = "}";
		mcastPort = begin + port + end;
		config.getProperties().put("mcast_port", mcastPort);

		// 修改IP
		String mcastIP = config.getProperties().get("mcast_addr");
		if (mcastIP != null) {
			begin = mcastIP.substring(0, mcastIP.indexOf(":") + 1);
			end = "}";
			mcastIP = begin + ip + end;
			config.getProperties().put("mcast_addr", mcastIP);
		}

		changeBindAddress(config);

		return configurator;
	}

	
	public static ProtocolStackConfigurator getJgroupsTCPConfigurator(
			String mip, int mport) {
		// 加载配置文件
		// File file = new File(ServiceConfig.rootPath+"/bin/"
		// + "jgroups-udp.xml");
		URL url = Utility.getUrl("jgroups-tcp.xml");
		ProtocolStackConfigurator configurator = null;

		try {
			configurator = ConfiguratorFactory.getStackConfigurator(url);

			// 修改端口
			ProtocolConfiguration config = getConfigurator(configurator,"MPING");

			String mcastPort = config.getProperties().get("mcast_port");
			String begin = null;
			String end = null;
			if (mcastPort != null) {
				begin = mcastPort.substring(0, mcastPort.indexOf(":") + 1);
				end = "}";
				mcastPort = begin + mport + end;
				config.getProperties().put("mcast_port", mcastPort);
			}

			// 修改IP
			String mcastIP = config.getProperties().get("mcast_addr");
			if (mcastIP != null) {
				begin = mcastIP.substring(0, mcastIP.indexOf(":") + 1);
				end = "}";
				mcastIP = begin + mip + end;
				config.getProperties().put("mcast_addr", mcastIP);
			}

			// 修改bind_addr
			changeTcpBindAddress(configurator);

		} catch (Exception e) {
			log.error("Get Jroup UDP Protocol String error", e);
			throw new RuntimeException(e);
		}
		return configurator;
	}


	
	/**
	 * 获得JGROUP的TCP_GOSSIP配置
	 * 
	 * @return
	 * 
	 * @2011-11-26 下午07:22:18
	 */
	public static ProtocolStackConfigurator getJgroupsTcpGossipConfigurator() 
	{
		// 加载配置文件
		URL url = Utility.getUrl("jgroups-tcpgossip.xml");
		ProtocolStackConfigurator configurator = null;

		try {
			configurator = ConfiguratorFactory.getStackConfigurator(url);

			String _hosts = ServerProperties.getString("tcp_gossip_host",null);
			int gossipPort = ServerProperties.getInt("gossip_port", -1);
			
			String[] hosts = _hosts.split(",");
			String initialHosts = "";
			for (String host : hosts) {
				initialHosts += host + "[" + gossipPort + "],";
			}
			initialHosts = initialHosts.substring(0, initialHosts.length() - 1);
			
			ProtocolConfiguration config = getConfigurator(configurator, "TCPGOSSIP");			
			config.getProperties().put("initial_hosts", initialHosts);
			
			// 修改端口
			int port = ServerProperties.getInt("group_transport_port", 7900);
			config = getConfigurator(configurator, "TCP");
			config.getProperties().put("bind_port",
					"${jgroups.tcp.port:" + port + "}");

			// 修改bind_addr
			changeTcpPingBindAddress(configurator);
			
			log.info("getJgroupsTcpGossipConfigurator:" + configurator.getProtocolStackString());

		} catch (Exception e) {
			log.error("Get Jroup Gossip Protocol String error", e);
			throw new RuntimeException(e);
		}
		return configurator;
	}

	
	/**
	 * 获得JGROUP的TCP配置
	 * 
	 * @return
	 * 
	 * @2011-11-26 下午07:22:18
	 */
	/*
	public static ProtocolStackConfigurator getJgroupsTcpPingDataConfigurator() {
		// 加载配置文件
		// File file = new File(ServiceConfig.rootPath+"/bin/"
		// + "jgroups-udp.xml");
		URL url = Utility.getUrl("jgroups-tcpping-data.xml");
		ProtocolStackConfigurator configurator = null;

		try {
			configurator = ConfiguratorFactory.getStackConfigurator(url);

			// 修改全部组员的IP
			String iplist = ServerProperties.getString("group_tcp_hosts",
					"127.0.0.1");
			int port = ServerProperties.getInt("group_datalayer_port", 7900);
			String[] hosts = iplist.split(",");
			String initialHosts = "";
			for (String host : hosts) {
				initialHosts += host + "[" + port + "],";
			}
			initialHosts = initialHosts.substring(0, initialHosts.length() - 1);
			ProtocolConfiguration config = configurator.getProtocolStack().get(
					1);
			config.getProperties().put("initial_hosts", initialHosts);

			// 修改端口
			config = configurator.getProtocolStack().get(0);
			config.getProperties().put("bind_port",
					"${jgroups.tcp.port:" + port + "}");

			// 修改bind_addr
			changeTcpPingBindAddress(configurator);
			
			log.info("getJgroupsTcpPingDataConfigurator:" + configurator.getProtocolStackString());

		} catch (Exception e) {
			log.error("Get Jroup UDP Protocol String error", e);
			throw new RuntimeException(e);
		}
		return configurator;
	}
	*/
	
	/**
	 * 搜尋指定的協議設定 
	 * @param config
	 * @param protocol
	 * @return
	 */
	private static ProtocolConfiguration getConfigurator(
			ProtocolStackConfigurator config, String protocol) {
		List<ProtocolConfiguration> list = config.getProtocolStack();
		if (list != null) {
			for (ProtocolConfiguration it : list) {
				if (protocol.equals(it.getProtocolName())) {
					return it;
				}
			}
		}
		return null;
	}

	/**
	 * 获得JGROUP的TCP配置<br>
	 * 
	 * jgroups-tcpping-tran.xml 這裡面的設定是jGroups官方定義好各項細部設定的樣本，<br>
	 * 這個method的作用在我們會將自定在server_info.properties的設定覆蓋xml的內容,<br>
	 * 例如IP, port<br>
	 * 
	 * 所以有一些關鍵字串必需一致, 才能找到和替換<br>
	 * @return
	 * 
	 * @2011-11-26 下午07:22:18
	 */
	public static ProtocolStackConfigurator getJgroupsTcpPingTranConfigurator() {
		// 加载配置文件
		// File file = new File(ServiceConfig.rootPath+"/bin/"
		// + "jgroups-udp.xml");
		URL url = Utility.getUrl("jgroups-tcpping-tran.xml");
		ProtocolStackConfigurator configurator = null;

		try {
			configurator = ConfiguratorFactory.getStackConfigurator(url);

			// 修改全部组员的IP
			String iplist = ServerProperties.getString("group_tcp_hosts",
					"127.0.0.1");
			
			int port = ServerProperties.getInt("group_transport_port", 7900);
			String[] hosts = iplist.split(",");
			String initialHosts = "";
			for (String host : hosts) {
				initialHosts += host + "[" + port + "],";
			}
			initialHosts = initialHosts.substring(0, initialHosts.length() - 1);
			
			ProtocolConfiguration config = getConfigurator(configurator, "TCPPING");			
			config.getProperties().put("initial_hosts", initialHosts);
			
			// 修改端口
			config = getConfigurator(configurator, "TCP");
			config.getProperties().put("bind_port",
					"${jgroups.tcp.port:" + port + "}");

			// 修改bind_addr
			changeTcpPingBindAddress(configurator);
			
			log.info("getJgroupsTcpPingTranConfigurator:" + configurator.getProtocolStackString());

		} catch (Exception e) {
			log.error("Get Jroup UDP Protocol String error", e);
			throw new RuntimeException(e);
		}
		return configurator;
	}

	/**
	 * 修改绑定IP
	 * 
	 * @param config
	 */
	private static void changeBindAddress(ProtocolConfiguration config) {
		String orgBindAddress = ServerProperties.getString(
				"group_bind_address", null);
		if (orgBindAddress != null) {
			String bindAddress = getBindAddress(orgBindAddress);
			log.info("group_bind_address:" + bindAddress);
			config.getProperties().put("bind_addr", bindAddress);
		}
		// 只有一个IP，就用这个了！
		else if (ipv4AddressList.size() == 1) {
			String bindAddress = ipv4AddressList.get(0);
			log.info("group_bind_address:" + bindAddress);
			config.getProperties().put("bind_addr", bindAddress);
		}
		// 多个IP，而且没有设定，则容易出问题
		else if (ipv4AddressList.size() > 1) {
			log.warn("mulit IP but not set to specified value :"
					+ ipv4AddressList);
		} else {
			log.warn("local IP:" + addressList);
		}
	}

	/**
	 * 修改TCP绑定IP
	 * 
	 * @param config
	 */
	private static void changeTcpBindAddress(ProtocolStackConfigurator config) {
		String orgBindAddress = ServerProperties.getString(
				"group_bind_address", null);
		if (orgBindAddress != null) {
			String bindAddress = getBindAddress(orgBindAddress);
			log.info("group_bind_address change from server_info:"
					+ bindAddress);
			config.getProtocolStack().get(0).getProperties().put("bind_addr",
					"${jgroups.tcp.address:" + bindAddress + "}");
			config.getProtocolStack().get(1).getProperties().put("bind_addr",
					"${jgroups.bind_addr:" + bindAddress + "}");
		}
		// 只有一个IP，就用这个了！
		else if (ipv4AddressList.size() == 1) {
			String bindAddress = ipv4AddressList.get(0);
			log.info("group_bind_address change from single ipv4:"
					+ bindAddress);
			config.getProtocolStack().get(0).getProperties().put("bind_addr",
					"${jgroups.tcp.address:" + bindAddress + "}");
			config.getProtocolStack().get(1).getProperties().put("bind_addr",
					"${jgroups.bind_addr:" + bindAddress + "}");
		}
		// 多个IP，而且没有设定，则容易出问题
		else if (ipv4AddressList.size() > 1) {
			log.warn("mulit IP but not set to specified value :"
					+ ipv4AddressList);
		} else {
			log.warn("local IP:" + addressList);
		}
	}

	/**
	 * 修改TCP绑定IP
	 * 
	 * @param config
	 */
	private static void changeTcpPingBindAddress(
			ProtocolStackConfigurator config) {
		String orgBindAddress = ServerProperties.getString(
				"group_bind_address", null);
		if (orgBindAddress != null) {
			String bindAddress = getBindAddress(orgBindAddress);
			log.info("group_bind_address change from server_info:"
					+ bindAddress);
			config.getProtocolStack().get(0).getProperties().put("bind_addr",
					"${jgroups.tcp.address:" + bindAddress + "}");
		}
		// 只有一个IP，就用这个了！
		else if (ipv4AddressList.size() == 1) {
			String bindAddress = ipv4AddressList.get(0);
			log.info("group_bind_address change from single ipv4:"
					+ bindAddress);
			config.getProtocolStack().get(0).getProperties().put("bind_addr",
					"${jgroups.tcp.address:" + bindAddress + "}");
		}
		else if(specifiedInterfaceAddress != null)
		{
			String bindAddress = specifiedInterfaceAddress;
			log.info("group_bind_address change from specified ipv4:" + bindAddress);
			config.getProtocolStack().get(0).getProperties().put("bind_addr",
					"${jgroups.tcp.address:" + bindAddress + "}");			
		}
		// 多个IP，而且没有设定，则容易出问题
		else if (ipv4AddressList.size() > 1) {
			log.warn("mulit IP but not set to specified value :" + ipv4AddressList);
		} else {
			log.warn("local IP:" + addressList);
		}
	}

	/**
	 * 获得绑定IP
	 * 
	 * @return
	 * 
	 * @2012-1-13 下午06:57:41
	 */
	public static String getBindAddress(String orgBindAddress) {
		if (orgBindAddress != null) {

			// loop
			if (orgBindAddress.equals("127.0.0.1")) {
				return orgBindAddress;
			}

			String[] orgValues = orgBindAddress.split("\\.");

			String bindAddress = null;
			for (String localIP : addressList) {

				boolean fire = true;

				String[] destValues = localIP.split("\\.");
				for (int i = 0; i < destValues.length; i++) {
					String orgs = destValues[i];

					// 星号是通配符
					if (orgValues[i].equals("*")) {
						continue;
					}
					// 否则要一样！
					else if (!orgs.equals(orgValues[i])) {
						fire = false;
						break;
					}
				}

				// 找到了
				if (fire) {
					bindAddress = localIP;
					break;
				}
			}

			if (bindAddress == null) {
				log.warn("bind_addr [" + orgBindAddress
						+ "] is not find! localIP:" + addressList);
			}

			return bindAddress;
		}
		return null;
	}

	public static void main(String[] args) {
		getJgroupsUDPConfigurator("", 0);
	}

}
